using System.Collections.Generic;
using MLSharp;
using MLSharp.Clustering;
using System.Linq;

namespace KnowledgeFrontierMiner
{
	/// <summary>
	/// Wraps <see cref="IKnowledgeFrontier"/> and <see cref="IClusterNode"/>
	/// into a single view.  Not every instance of this type will actually be a knowledge frontier. 
	/// </summary>
	internal class KnowledgeFrontierView : IClusterNode
	{
		#region Private Fields

		/// <summary>
		/// The node being wrapped.
		/// </summary>
		private readonly IClusterNode mClusterNode;

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns true if the node is not a knowledge frontier, false if it is 
		/// a knowledge frontier.
		/// </summary>
		public bool IsNotKnowledgeFrontier
		{
			get { return KnowledgeFrontier == null; }
		}

		/// <summary>
		/// The average accuracy across instances of the node.
		/// </summary>
		public double AverageAccuracy
		{
			get
			{
				if (KnowledgeFrontier != null)
				{
					return KnowledgeFrontier.AverageAccuracy;
				}
				else
				{
					//Calculate it on the fly.
					return Instances.Sum(i => i.Values[AccuracyIndex])/Instances.Count;
				}
			}
		}

		/// <summary>
		/// Gets the standard deviation of accuracy.
		/// </summary>
		public double AccuracyStdDev
		{
			get
			{
				if (KnowledgeFrontier != null)
				{
					return KnowledgeFrontier.AccuracyStdDev;
				}
				else
				{
					Stats stats = new Stats();

					foreach (Instance instance in Instances)
					{
						stats.Add(instance.Values[AccuracyIndex]);
					}

					stats.CalculateDerived();

					return stats.StdDev;
				}
			}
		}

		/// <summary>
		/// Gets the original cluster node.
		/// </summary>
		public IClusterNode Original
		{
			get
			{
				return mClusterNode;
			}
		}

		#endregion

		#region Public Constructors

		/// <summary>
		/// Creates a view over the specified node.
		/// </summary>
		/// <param name="node"></param>
		/// <param name="accuracyIndex"></param>
		/// <param name="ds">The dataset the cluster was derived from.</param>
		public KnowledgeFrontierView(IClusterNode node, int accuracyIndex, IDataSet ds)
		{
			mClusterNode = node;

			AccuracyIndex = accuracyIndex;

			Children = new List<IClusterNode>();

			DataSet = ds;
		}

		#endregion

		#region Implementation of IClusterNode

		/// <summary>
		/// The instances that belong to the cluster node.
		/// </summary>
		public IList<Instance> Instances
		{
			get { return mClusterNode.Instances; }
		}

		/// <summary>
		/// The child nodes of the current node.
		/// </summary>
		public IList<IClusterNode> Children { get; set; }

		/// <summary>
		/// The ID of the node.
		/// </summary>
		public int NodeId
		{
			get { return mClusterNode.NodeId; }
		}

		/// <summary>
		/// The knowledge frontier, if any.
		/// </summary>
		public IKnowledgeFrontier KnowledgeFrontier { get; set; }

		/// <summary>
		/// The dataset the cluster was derived from (attributes
		/// are needed by some of the users of this view).
		/// </summary>
		public IDataSet DataSet { get; private set; }

		/// <summary>
		/// The index of the accuracy attribute.
		/// </summary>
		public int AccuracyIndex { get; private set; }

		#endregion
	}
}